import logging
import os
import re
import sys
from datetime import timedelta, datetime
from logging.handlers import BaseRotatingHandler
from pathlib import PurePath, Path
from stat import ST_MTIME


class TimedRotatingFileHandler(BaseRotatingHandler):
    """
    Handler for logging to a file, rotating the log file at certain timed
    intervals.

    If backupCount is > 0, when rollover is done, no more than backupCount
    files are kept - the oldest ones are deleted.
    """

    def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, atTime=None):
        file_path = Path(__file__).parent.parent.parent / PurePath(filename)
        base_path = Path(file_path.parent)
        if not base_path.exists():
            base_path.mkdir(parents=True)
        BaseRotatingHandler.__init__(self, filename, 'a+', encoding, delay)
        self.when = when.upper()
        self.backupCount = backupCount

        self.atTime = atTime
        if self.when == 'S':
            self.interval = timedelta(seconds=1)  # one second
            self.suffix = "%Y-%m-%d_%H-%M-%S"
            self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$"
        elif self.when == 'M':
            self.interval = timedelta(minutes=1)  # one minute
            self.suffix = "%Y-%m-%d_%H-%M"
            self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}(\.\w+)?$"
        elif self.when == 'H':
            self.interval = timedelta(hours=1)  # one hour
            self.suffix = "%Y-%m-%d_%H"
            self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}(\.\w+)?$"
        elif self.when == 'D' or self.when == 'MIDNIGHT':
            self.interval = timedelta(days=1)  # one day
            self.suffix = "%Y-%m-%d"
            self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
        else:
            raise ValueError(
                "Invalid rollover interval specified: %s" % self.when)
        self.extMatch = re.compile(self.extMatch, re.ASCII)
        self.interval = self.interval * interval
        filename = self.baseFilename
        if os.path.exists(filename):
            t = os.stat(filename)[ST_MTIME]
            t = datetime.fromtimestamp(t)
        else:
            t = datetime.now()
        self.rolloverAt = self.computeRollover(t)

    def computeRollover(self, currentTime):
        """
        Work out the rollover time based on the specified time.
        """
        if self.when != "S":
            currentTime = currentTime.replace(microsecond=0).replace(second=0)
        result = currentTime + self.interval
        return result

    def shouldRollover(self, record):
        """
        Determine if rollover should occur.

        record is not used, as we are just comparing times, but it is needed so
        the method signatures are the same
        """
        t = datetime.now()
        if t >= self.rolloverAt:
            return 1
        return 0

    def getFilesToDelete(self):
        """
        Determine the files to delete when rolling over.

        More specific than the earlier method, which just used glob.glob().
        """
        dirName, baseName = os.path.split(self.baseFilename)
        fileNames = os.listdir(dirName)
        result = []
        prefix = baseName + "."
        plen = len(prefix)
        for fileName in fileNames:
            if fileName[:plen] == prefix:
                suffix = fileName[plen:]
                if self.extMatch.match(suffix):
                    result.append(os.path.join(dirName, fileName))
        if len(result) < self.backupCount:
            result = []
        else:
            result.sort()
            result = result[:len(result) - self.backupCount]
        return result

    def doRollover(self):
        """
        do a rollover; in this case, a date/time stamp is appended to the filename
        when the rollover happens.  However, you want the file to be named for the
        start of the interval, not the current time.  If there is a backup count,
        then we have to get a list of matching filenames, sort them and remove
        the one with the oldest suffix.
        """
        if self.stream:
            self.stream.close()
            self.stream = None
        currentTime = datetime.now()
        t = self.rolloverAt - self.interval
        dfn = self.rotation_filename(
            self.baseFilename + "." + t.strftime(self.suffix))
        if os.path.exists(dfn):
            os.remove(dfn)
        self.rotate(self.baseFilename, dfn)
        if self.backupCount > 0:
            for s in self.getFilesToDelete():
                os.remove(s)
        if not self.delay:
            self.stream = self._open()
        newRolloverAt = self.computeRollover(currentTime)
        while newRolloverAt <= currentTime:
            newRolloverAt = newRolloverAt + self.interval
        self.rolloverAt = newRolloverAt


class ErrorFilter(logging.Filter):
    """日志过滤器 仅保存错误日志"""

    def filter(self, record: logging.LogRecord) -> bool:
        return record.levelno == logging.ERROR


class InfoFilter(logging.Filter):
    """日志过滤器 仅保存基础输出日志"""

    def filter(self, record: logging.LogRecord) -> bool:
        return record.levelno == logging.INFO


class WarningFilter(logging.Filter):
    """日志过滤器 仅保存警告日志"""

    def filter(self, record: logging.LogRecord) -> bool:
        return record.levelno == logging.WARNING


FORMATTERS = {
    'general': {'format': '%(asctime)s %(filename)s [%(funcName)s:%(lineno)s]: %(message)s'},
    'detail': {'format': '%(asctime)s %(levelname)s %(filename)s [%(funcName)s:%(lineno)s]: %(message)s'},
    'simple': {'format': '%(asctime)s %(message)s'},
}
FILTERS = {
    'info': {'()': 'chatterbot.zyrobot.logger_config.InfoFilter'},
    'warning': {'()': 'chatterbot.zyrobot.logger_config.WarningFilter'},
    'error': {'()': 'chatterbot.zyrobot.logger_config.ErrorFilter'}
}

when = 'h'
backupCount = 7*24

CONSOLE_HANDLER = {
    'level': 'DEBUG',
    'class': 'logging.StreamHandler',
    'formatter': 'detail',
    'stream': 'ext://sys.stdout'
}
SIMPLE_CONSOLE_HANDLER = {
    'level': 'DEBUG',
    'class': 'logging.StreamHandler',
    'formatter': 'simple',
    'stream': 'ext://sys.stdout'
}

GENERAL_ALL_HANDLER = {
    'level': 'INFO',
    'formatter': 'detail',
    'class': 'chatterbot.zyrobot.logger_config.TimedRotatingFileHandler',
    'filename': 'log/web/all.log',
    'when': when,
    'backupCount': backupCount,
    'delay': True
}
GENERAL_INFO_HANDLER = {
    'level': 'INFO',
    'filters': ['info'],
    'formatter': 'general',
    'class': 'chatterbot.zyrobot.logger_config.TimedRotatingFileHandler',
    'filename': 'log/web/info.log',
    'when': when,
    'backupCount': backupCount,
    'delay': True
}
GENERAL_WARNING_HANDLER = {
    'level': 'INFO',
    'filters': ['warning'],
    'formatter': 'detail',
    'class': 'chatterbot.zyrobot.logger_config.TimedRotatingFileHandler',
    'filename': 'log/web/warning.log',
    'when': when,
    'backupCount': backupCount,
    'delay': True
}
GENERAL_ERROR_HANDLER = {
    'level': 'INFO',
    'filters': ['error'],
    'formatter': 'detail',
    'class': 'chatterbot.zyrobot.logger_config.TimedRotatingFileHandler',
    'filename': 'log/web/error.log',
    'when': when,
    'backupCount': backupCount,
    'delay': True
}

SQL_INFO_HANDLER = {
    'level': 'INFO',
    'filters': ['info'],
    'formatter': 'simple',
    'class': 'chatterbot.zyrobot.logger_config.TimedRotatingFileHandler',
    'filename': 'log/sql/info.log',
    'when': when,
    'backupCount': backupCount,
    'delay': True
}
SQL_ERROR_HANDLER = {
    'level': 'INFO',
    'filters': ['error'],
    'formatter': 'detail',
    'class': 'chatterbot.zyrobot.logger_config.TimedRotatingFileHandler',
    'filename': 'log/sql/error.log',
    'when': when,
    'backupCount': backupCount,
    'delay': True
}

QA_LOG_HANDLER = {
    'level': 'INFO',
    'formatter': 'general',
    'class': 'chatterbot.zyrobot.logger_config.TimedRotatingFileHandler',
    'filename': 'log/qa/qa.log',
    'when': when,
    'backupCount': backupCount,
    'delay': True
}
TIME_INFO_HANDLER = {
    'level': 'INFO',
    'formatter': 'simple',
    'class': 'chatterbot.zyrobot.logger_config.TimedRotatingFileHandler',
    'filename': 'log/time/time.log',
    'when': when,
    'backupCount': backupCount,
    'delay': True
}
TR_LOG_HANDLER = {
    'level': 'INFO',
    'formatter': 'detail',
    'class': 'chatterbot.zyrobot.logger_config.TimedRotatingFileHandler',
    'filename': 'log/translate/translate.log',
    'when': when,
    'backupCount': backupCount,
    'delay': True
}
HANDLERS = {
    'general_all': GENERAL_ALL_HANDLER,
    'general_console': CONSOLE_HANDLER,
    'general_info': GENERAL_INFO_HANDLER,
    'general_warning': GENERAL_WARNING_HANDLER,
    'general_error': GENERAL_ERROR_HANDLER,
    'sql_error': SQL_ERROR_HANDLER,
    'sql_info': SQL_INFO_HANDLER,
    'qa_log': QA_LOG_HANDLER,
    'time_info': TIME_INFO_HANDLER,
    'simple_console': SIMPLE_CONSOLE_HANDLER,
    'tr_log': TR_LOG_HANDLER,
}
WEB_LOGGER = {
    'level': 'DEBUG',
    'handlers': ['general_console', 'general_info', 'general_warning', 'general_error', 'general_all']
}
SQL_LOGGER = {
    'level': 'INFO',
    'handlers': ['simple_console', 'sql_info', 'sql_error']
}
QA_LOGGER = {
    'level': 'INFO',
    'handlers': ['general_console', 'qa_log']
}
TIME_LOGGER = {
    'level': 'INFO',
    'handlers': ['time_info']
}
VOICE_LOGGER = {
    'level': 'DEBUG',
    'handlers': ['general_console', 'tr_log']
}
LOGGERS = {
    'web_logger': WEB_LOGGER,  # 请求及基础流程
    'sql_logger': SQL_LOGGER,  # 查询语句
    'qa_logger': QA_LOGGER,  # 质检任务
    'time_logger': TIME_LOGGER,  # 时间检测
    'voice_logger': VOICE_LOGGER,  # 翻译流程记录
}
